{"cells":[{"attachments":{},"cell_type":"markdown","metadata":{"id":"11n5gndbRzoY"},"source":["# Sequences: Lists and Tuples\n"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"AIFsv_RZ1iV0"},"source":["\n"," \n"," \n","
\n"," \"Open\n"," \n"," \n","
"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["One more topic you’ll need to understand before you can begin writing programs in earnest is the `list` data type and its cousin, the `tuple`. `Lists` and `tuples` can contain multiple values, which makes writing programs that handle large amounts of data easier. These data types are called ***containers***, meaning they are objects that “contain” other objects. They each have some important distinguishing properties and come with their own set of ***methods*** for interacting with objects of each type. `List` and `tuple` belong to ***sequence*** data types, which means they represent **ordered collections of items**. They share the same characteristic as `string` and the `range` object returned by `range()` function. Many of the capabilities shown in this chapter apply to all sequence types."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["> Checkout https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range for more details."]},{"cell_type":"markdown","metadata":{"id":"MTJO4K049nuU"},"source":["## List"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"iyiuJDms-ZlA"},"source":["In a `string`, the values are characters; in a `list`, they can be any type. The values in a `list` are called ***elements*** or sometimes ***items***. Items are separated with commas.\n","\n","
\n","
source: https://favtutor.com/blogs/list-vs-dictionary
\n","\n","There are several ways to create a new `list`; the simplest is to enclose the elements in square brackets (“[” and ”]”). A `list` that contains no elements is called an empty `list`; you can create one with empty brackets."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"_UZvUFCrqFdC"},"outputs":[],"source":["type([])"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"iA6_fB0H9onD"},"outputs":[],"source":["type([10, 20, 30, 40]), type(['calculus', 'introduction to mathematics', 'computer programming', 'linear algebra'])"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"jPqRNKXB-03e"},"source":["The first example is a list of four integers and the second is a list of four strings. "]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### Getting Individual Values in a List with Indexes"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"srlcPSxA_DhN"},"source":["You can reference a `list` item by writing the `list`’s name followed by the element’s ***index*** (that is, its position number) enclosed in square brackets (`[]`, known as the ***subscription operator*** or ***bracket operator***). Remember that the indices start at 0:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"wrpckWiU-9W8"},"outputs":[],"source":["subjects = ['calculus', 'introduction to mathematics', 'computer programming', 'linear algebra']\n","print(subjects[0])\n","print(subjects[3])"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["**Note that the first index is 0**, the last index is one less than the size of the `list`; a `list` of four items has 3 as its last index."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Python will give you an `IndexError` error message if you use an index that exceeds the number of values in your list value."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(subjects[4])"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The elements of a `list` don’t have to be the same type. The following `list` contains a `string`, a `float`, an `integer`, and another `list`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam = ['spam', 2.0, 5, [10, 20]]"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The values in these lists of lists can be accessed using multiple indexes:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam[3][1]"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The first index dictates which items in the outer `list` to use, and the second indicates the value within the inner `list`. If you only use one index like `spam[3]`, the program will print the entire list value at that index."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam[3]"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Negative Indexes"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["While indexes start at 0 and go up, you can also use negative integers for the index. The integer value -1 refers to the last index in a `list`, the value -2 refers to the second-to-last index in a `list`, and so on."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(subjects[-1]) # subjects = ['calculus', 'introduction to mathematics', 'computer programming', 'linear algebra']\n","print(subjects[-2])"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Getting a `List`’s Length with the `len()` Function"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The `len()` function will return the number of values that are in a `list`, just like it can count the number of characters in a string."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["len(subjects)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Getting a sublist from Another `List` with Slices"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Just as an index can get a single value from a `list`, a ***slice*** can get several values from a `list` as a **new list**. A slice is typed between square brackets, like an index, but has two integers separated by a colon. "]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["- `subjects[2]` is a list with an index.\n","- `subjects[1:3]` is a list with a slice."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["In a slice, the first integer is the index where the slice starts. The second integer is the index where the slice ends. A slice goes up to, but will not include, the value at the second index. A slice evaluates to a new `list`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["subjects = ['calculus', 'introduction to mathematics', 'computer programming', 'linear algebra']\n","print(subjects[0:3])\n","print(subjects[1:-1])"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["As a shortcut, you can leave out one or both indexes on either side of the colon in the slice. Leaving out the first index is the same as using 0 or the beginning of the `list`. Leaving out the second index is the same as using the length of the `list`, which will slice to the end of the `list`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(subjects[:3]) # same as subjects[0:3] \n","print(subjects[1:]) # same as subjects[1:len(s)] \n","print(subjects[:]) # same as s[0:len(s)]"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Just like `range()`, slicing has the optional third index that can be used to specify the step. "]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(subjects[::2]) # Note the default step is 1\n","print(subjects[::-1]) # Reverse the order of the list"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Changing Values in a `List` with Indexes"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"Ylv8bDoR_uZe"},"source":["Unlike `strings`, `lists` are ***mutable*** because you can reassign an item in a `list`. When the bracket operator appears on the left side of an assignment, it identifies the element of the `list` that will be assigned."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"xtumYSz9_M-s"},"outputs":[],"source":["numbers = [17, 123, 42, 7]\n","numbers[1] = 5\n","numbers"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"0K-ICEPo_4rQ"},"source":["The first element of numbers, which used to be 123, is now 5.\n","\n","All in all, you can think of a `list` as a relationship between indices and elements. This relationship is called a mapping; each index \"maps to\" one of the elements."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### `List` Concatenation and `List` Replication"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Lists can be concatenated and replicated just like strings. The `+` operator combines two lists to create a new `list` and the `*` operator can be used with a `list` and an integer value to replicate the `list`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["[1, 2, 3] + ['A', 'B', 'C']"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["['X', 'Y', 'Z'] * 3"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Removing Values from Lists with `del` Statements"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The `del` statement will delete values at an index in a `list`. All values in the `list` after the deleted value will be moved up one index."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["t = ['a', 'b', 'c']\n","del t[1] # using index\n","print(t)"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"IvviPig3At12"},"source":["### `List` traversal"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"JwenWwmtAy78"},"source":["In Chapter 2, you have learned about using `for` loops to execute a block of code a certain number of times. Technically, a `for` loop repeats the code block once for each item in a sequence."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for i in range(4):\n"," print(i)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(range(4))\n","list(range(4))"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["This is because the return value from `range(4)` is a sequence that Python considers similar to `[0, 1, 2, 3]`. The following program has the same output as the previous one:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for i in [0, 1, 2, 3]:\n"," print(i)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"9X-CrgFjAjZM"},"outputs":[],"source":["for subject in subjects: # subjects = ['calculus', 'introduction to mathematics', 'computer programming', 'linear algebra']\n"," print(subject)"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"OYn3-mOZA_WV"},"source":["This works well if you only need to read the elements of the `list`. But you need the indices that you want to write or update the elements. A common way to do that is to combine the functions `range()` and `len()`:"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["A common `Python` technique is to use `range(len(someList))` with a `for` loop to iterate over the indexes of a list."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for i in range(len(numbers)): # numbers = [17, 5, 42, 7]\n"," print(i, numbers[i])\n"," numbers[i] = numbers[i]**2\n","\n","print(numbers)"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"720JXEBDBQhl"},"source":["This loop traverses the list and prints each element. `len()` returns the number of elements in the list. `range()` returns a list of indices from `0` to `n − 1`, where `n` is the length of the list. Each time through the loop, `i` gets the index of the next element. This is handy since it will iterate through all the indexes, no matter how many items it contains."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### The `in` and `not in` Operators"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["You can determine whether an object is or isn’t in a `list` with the `in` and `not in` operators. These expressions will evaluate to a `Boolean` value."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print('howdy' in ['hello', 'hi', 'howdy', 'heyas'])\n","print('English' not in subjects)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Using the `enumerate()` Function with Lists"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"9aL7ZSywRpjY"},"source":["Instead of using the `range(len(someList))` technique with a `for` loop to obtain the integer index of the items in the `list`, you can call the `enumerate()` function instead. On each iteration of the loop, `enumerate()` will return two values: **the index of the item and the item itself.**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"PILF96yHRu83"},"outputs":[],"source":["numbers = [17, 5, 42, 7]\n","\n","for i, number in enumerate(numbers): \n"," print(i, number)\n"," numbers[i] = number**2\n","\n","print(numbers)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Loop in Multiple `Lists` with `zip()`"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Built-in function `zip()` enables you to iterate over multiple sequences of data at the same time. The function receives as arguments any number of sequences and returns an iterator that produces `tuples` containing the elements at the same index in \n","each."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["names = ['Bob', 'Sue', 'Amanda']\n","grade_point_averages = [3.5, 4.0, 3.75] \n","\n","for name, gpa in zip(names, grade_point_averages):\n"," print('Name=', name, 'GPA=', gpa)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The above snippet call `zip()` to produces the tuples `('Bob', 3.5)`, `('Sue', 4.0)` and `('Amanda', 3.75)` consisting of the elements at index 0, 1 and 2 of each `list`, respectively. Note that we unpack (which we will elaborate later on) each tuple into `name` and `gpa` and display them."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["list(zip(names, grade_point_averages))"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### Methods of the `list`"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["A ***method***, introduced in Chapter 1, is the same as a function, except it is \"called on\" an object. For example, if a `list` object were stored in `spam`, you would call the `index()` list method on that `list` like so: `spam.index('hello')`. The method part comes after the object, separated by a period.\n","\n","Each data type has its own set of methods. The `list` data type, for example, has several useful methods for finding, adding, removing, and otherwise manipulating values in a `list`."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Adding elements to `Lists` with the `append()` and `insert()` Methods"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"kCDRoMiLdFn7"},"source":["`append()` adds a new element to the end of a `list`:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"yzO9iDDwc-eK"},"outputs":[],"source":["t = ['a', 'b', 'c']\n","t.append('d')\n","t # in-place operation!"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The previous `append()` method call adds the argument to the end of the `list`. The `insert()` method can insert an element at any index in the `list`. The first argument to `insert()` is the index for the new value, and the second argument is the new value to be inserted."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["t = ['a', 'b', 'c']\n","t.insert(1,'e')\n","t # in-place operation!"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["> Notice that the code is `t.append('d')` and `t.insert(1, 'e')`, not `t = t.append('d')` and `t = t.insert(1, 'e')`. In fact, the return value of `append()` and `insert()` is `None`, so you definitely wouldn’t want to store this as the new variable value. Rather, the `list` is modified in-place. "]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Methods belong to a single data type. The `append()` and `insert() `methods are `list` methods and can be called only on `list` object, not on other objects such as `strings` or `integers`. "]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["eggs = 'hello'\n","eggs.append('world')"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Adding all the elements of a `List` to the end of `List` with the `extend()` Methods"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Use `list` method `extend()` to add all the elements of another sequence to the end of a list:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["color_names = ['orange', 'yellow', 'green']\n","color_names.extend(['indigo', 'violet']) # equivalent to color_names += ['indigo', 'violet']"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["color_names"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Removing elements from `Lists` with the `remove()` Method"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The `remove()` method will pass the object to be removed from the `list` when it is called:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam = ['cat', 'bat', 'rat', 'elephant']\n","spam.remove('bat')\n","print(spam)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["> The `del` statement is good to use when you know the index of the element you want to remove from the `list`. The `remove()` method is useful when you know the element you want to remove from the list."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Sorting the elements in a `List` with the `sort()` Method"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Lists of numbers or lists of strings can be sorted with the `sort()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jlIhCLtodaKM"},"outputs":[],"source":["spam = [2, 5, 3.14, 1, -7]\n","spam.sort() # The default behavior is sorting in ascending order\n","print(spam)\n","\n","spam = ['ants', 'cats', 'dogs', 'badgers', 'Elephants']\n","spam.sort()\n","print(spam)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Note that `sort()` uses “ASCII order” rather than alphabetical order for sorting strings. This means uppercase letters come before lowercase letters. Therefore, the lowercase a is sorted so that it comes after the uppercase Z."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["You can also pass `True` for the `reverse` keyword argument to have `sort()` sort the values in reverse order."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam.sort(reverse=True) # Sort in descending order\n","print(spam)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["#### Searching an element in a `List` with the `index()` Method"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["`List` objects have an `index()` method that accepts an argument, and if that argument exists in the list, the index of the argument is returned. If the argument isn’t in the `list`, then `Python` produces a `ValueError` error."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam = ['hello', 'hi', 'howdy', 'heyas']\n","spam.index('hi')"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam = ['hello', 'hi', 'howdy', 'heyas']\n","spam.index('world')"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["When there are duplicates of the elements in the `list`, the index of its first appearance is returned."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam = ['Zophie', 'Pooka', 'Fat-tail', 'Pooka']\n","spam.index('Pooka')"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### Numerical functions for `list`"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"RhDCtHL5eKyQ"},"source":["There are a number of built-in functions that can be used on `lists` that allow you to quickly look through a `list` without writing your own loops:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"A01f6wX1d9UV"},"outputs":[],"source":["nums = [3, 41, 12, 9, 74, 15]\n","print(len(nums))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"SGUBu86tePD2"},"outputs":[],"source":["print(max(nums))\n","print(min(nums))\n","print(sum(nums))"]},{"cell_type":"markdown","metadata":{"id":"pLBqUakio6cY"},"source":["Check out https://docs.python.org/3/tutorial/datastructures.html#more-on-lists for more methods!"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### `List` Comprehensions"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Consider how you might make a `list` of the first 10 square numbers (that is, the square of each integer from 1 through 10)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["squares = []\n","for value in range(1,11):\n"," squares.append(value**2)\n","print(squares)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["But a ***list comprehension*** allows you to generate this same list in just one line of code. A list comprehension combines the `for` loop and the creation of new elements into one line, and automatically appends each new element!"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["squares = [value**2 for value in range(1, 11)]\n","print(squares)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["To use this syntax\n","\n","- Begin with a descriptive name for the `list`, such as `squares`. \n","- Next, open a set of square brackets and define the expression for the values you want to store in the new `list`. In this example, the expression is `value**2`\n","- Then, write a `for` loop to generate the numbers you want to feed into the expression and close the square brackets. In this example, the `for` loop iterates value in `range(1, 11)`, which feeds the values 1 through 10 into the expression `value**2`. \n","\n","Note that no colon is used at the end of the `for` statement."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["> The syntax of list comprehension is similar to the set-builder notation. For instance, preivous example is similar to $\\{x^2 | x \\in \\{1,2,...,10\\}\\}$"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Another common operation is filtering elements to select only those that match a condition. This typically produces a `list` with fewer elements than the data being filtered. To do this in a `list` comprehension, use the `if` clause. The following\n","includes in `list1` only the even values produced by the `for` clause:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["list1 = [item for item in range(1, 11) if item % 2 == 0]\n","list1"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["> ### Exercise 1: Bulls and Cows (or 1A2B) is a code-breaking game. The numerical version of the game is usually played with four digits. On a sheet of paper, the players each write a 4-digit secret number. The digits must all be different. Then, in turn, the players try to guess their opponent's number, which gives the number of matches. If the matching digits are in their right positions, they are \"bulls\" (A). If they are in different positions, they are \"cows\" (B). For example, if the secret number is 4271 and our guess is 1234, then we should get 1 bull and 2 cows. (The bull is \"2\", the cows are \"4\" and \"1\".). Please complete the following game design, that the computer will generate a 4-digit number, and we must write a function to read the user's 4-digit inputs and check the user's guess against the secret number. Finally, return the message XAXB to the user.\n","\n","
\n","
source: https://en.wikipedia.org/wiki/Bulls_and_Cows
"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["%%writefile 1A2B.py\n","\n","import random\n","\n","# Generate a random four-digit number\n","def generate_number():\n"," digits = list(range(10))\n"," random.shuffle(digits) # randomly shuffle the list!\n"," return digits[:4]\n","\n","# Check the user's guess against the secret number\n","def check_guess(guess, secret):\n"," # Note that both guess and secret are lists!\n"," a = 0 # number of correct digits in the correct position\n"," b = 0 # number of correct digits in the wrong position\n"," for i,j in zip(__,__): # Iterate over two lists\n"," if i == j:\n"," a += 1\n"," elif __________: # Use operator to determine whether the digit is in secret number or not\n"," b += 1\n"," return a, b\n","\n","# Play the game\n","print(\"Welcome to 1A2B!\")\n","print(\"I'm thinking of a four-digit number. Can you guess it?\")\n","secret = generate_number()\n","guesses = 0\n","while True:\n"," guess = input(\"Enter your guess, enter 'quit' to give up: \")\n"," if guess == 'quit':\n"," print(\"The secret number is\", secret)\n"," break\n"," elif len(guess) != 4 or not guess.isdigit():\n"," print(\"Invalid guess. Please enter a four-digit number.\")\n"," continue\n"," guess = _______ # Use list comprehension to get the 4-digit guess list\n"," guesses += 1\n"," result = check_guess(guess, secret)\n"," print(result[0],'A', result[1], 'B', sep=\"\")\n"," if result[0] == 4:\n"," print(\"Congratulations, you guessed the number in\", guesses, \"guesses!\")\n"," break"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["%%writefile 1A2B.py\n","\n","import random\n","\n","# Generate a random four-digit number\n","def generate_number():\n"," digits = list(range(10))\n"," random.shuffle(digits) # randomly shuffle the list!\n"," return digits[:4]\n","\n","# Check the user's guess against the secret number\n","def check_guess(guess, secret):\n"," # Note that both guess and secret are lists!\n"," a = 0 # number of correct digits in the correct position\n"," b = 0 # number of correct digits in the wrong position\n"," for i,j in zip(guess, secret):\n"," if i == j:\n"," a += 1\n"," elif i in secret:\n"," b += 1\n"," return a, b\n","\n","# Play the game\n","print(\"Welcome to 1A2B!\")\n","print(\"I'm thinking of a four-digit number. Can you guess it?\")\n","secret = generate_number()\n","guesses = 0\n","while True:\n"," guess = input(\"Enter your guess, enter 'quit' to give up: \")\n"," if guess == 'quit':\n"," print(\"The secret number is\", secret)\n"," break\n"," elif len(guess) != 4 or not guess.isdigit():\n"," print(\"Invalid guess. Please enter a four-digit number.\")\n"," continue\n"," guess = [int(s) for s in guess] # Use list comprehension to get the 4-digit guess list\n"," guesses += 1\n"," result = check_guess(guess, secret)\n"," print(result[0],'A', result[1], 'B', sep=\"\")\n"," if result[0] == 4:\n"," print(\"Congratulations, you guessed the number in\", guesses, \"guesses!\")\n"," break"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### Sequence Data Types"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["`Lists` aren’t the only data types that represent ordered sequences of values. For example, `strings` and `lists` are similar if you consider a string to be a \"list\" of single text characters. The `Python` sequence data types include `lists`, `strings`, range objects returned by `range()`, and `tuples`. Many of the things you can do with `lists` can also be done with `strings` and other values of sequence types: indexing; slicing; and using them with for loops, with `len()`, and with the `in` and `not in` operators."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["'a' in 'apple'"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### Mutable and Immutable Data Types"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["But `lists` and `strings` are different in an important way. A list object is a ***mutable*** data type: it can have elements added, removed, or changed. However, a string is ***immutable***: it cannot be changed. Trying to reassign a single character in a string results in a `TypeError` error:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["name = 'Zophie a cat'\n","name[7] = 't'"]},{"cell_type":"markdown","metadata":{"id":"UMo588t4rY-O"},"source":["## Tuples"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"3cbVny0qrb2X"},"source":["A `tuple` is a sequence of values much like a `list`. The values stored in a `tuple` can be any type, and they are indexed by integers. The important difference is that `tuples` are ***immutable***.\n","\n","> It is similar to the tuple you encounter in math"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"ZBmWzbZrrodF"},"source":["Although it is not necessary, it is common to enclose `tuples` in parentheses to help us quickly identify `tuples` when we look at `Python` code:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"31nRYo3kp8pZ"},"outputs":[],"source":["type(())"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"byFxBdL-p0W7"},"outputs":[],"source":["t = ('a', 'b', 'c', 'd', 'e')\n","type(t)"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"ljwYA7Bnrtfi"},"source":["To create a `tuple` with a single element, you have to include the final comma or use the `tuple()` function:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"7bKaCxd6rm3o"},"outputs":[],"source":["t1 = ('a',)\n","print(type(t1))\n","t2 = ('a')\n","print(type(t2))"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"49oF_8Z_r-jU"},"source":["If the argument of `tuple()` is a sequence (`string`, `list`, or `tuple`), the result is a `tuple` with the elements of the sequence:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"HpZ5N3P-r4NB"},"outputs":[],"source":["t = tuple('nsysu')\n","t"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"uCQoFLN6sFSN"},"source":["Most `list` operators also work on `tuples`. The bracket operator indexes an element:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"qZmPRU1OsBxR"},"outputs":[],"source":["print(t[0])\n","print(t[1:3])"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"1_iwRUm4sNKE"},"source":["But if you try to modify one of the elements of the `tuple`, you get an error:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"bZZIW_AssHyW"},"outputs":[],"source":["t[0] = 'A'"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["You can use `tuples` to convey to anyone reading your code that you don’t intend for that sequence of values to change. Use a `tuple` if you need an ordered sequence of values that never changes."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### Unpacking Sequences"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We have seen the multiple assignment trick in the previous chapter (which is actually unpacking the `tuple`). In fact, you can unpack any sequence’s elements by assigning the sequence to a comma-separated list of variables."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["student_tuple = ('Amanda', [98, 85, 87])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["first_name, grades = student_tuple\n","print(first_name, grades)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Unpacking is widely used to return multiple values in a function:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def total_ave(grade):\n"," total = sum(grade)\n"," ave = total/len(grade)\n"," return total, ave\n","\n","grades = [85, 70, 100, 90]\n","total, ave = total_ave(grades)\n","\n","print(total, ave)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["## References"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Technically, in `Python`, variables store ***references*** to the computer memory locations where the values are stored."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam = 42\n","cheese = spam\n","print(id(cheese), id(spam))\n","spam = 100\n","print(id(cheese), id(spam))\n","\n","spam, cheese"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["When you assign 42 to the `spam` variable, you are actually creating the 42 value in the **computer’s memory and storing a reference (address)** to it in the `spam` variable. When you copy the value in `spam` and assign it to the variable `cheese`, you are actually copying the reference. Both the `spam` and `cheese` variables refer to the 42 value in the computer’s memory. When you later change the value in `spam` to 100, you’re creating a new 100 value and storing a reference to it in `spam`. This doesn’t affect the value in `cheese`. Integers are immutable values that don’t change; changing the `spam` variable is actually making it refer to a completely different value in memory. \n","\n","You can use `id()` function to verify this behavior. In CPython (the most widely used implementation of Python), the identifier returned by `id()` is actually the memory address of the object, represented as a Python integer. All values in Python have a unique identity (address) that can be obtained with the `id()` function."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["But `lists` don’t work this way, because `list` are mutable:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["spam = [0, 1, 2, 3, 4, 5]\n","cheese = spam # The reference is being copied, not the list.\n","print(id(cheese), id(spam))\n","cheese[1] = 'Hello!' # This changes the list value.\n","print(id(cheese), id(spam))\n","\n","spam, cheese"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Using boxes as a metaphor for variables, the following shows what happens when a `list` is assigned to the `spam` variable."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["
\n","
source: https://automatetheboringstuff.com/2e/chapter4/
"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Then, the reference in `spam` is copied to `cheese`. Only a new reference was created and stored in `cheese`, not a new `list`. Note how both references refer to the same `list`."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["
\n","
source: https://automatetheboringstuff.com/2e/chapter4/
"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["When you alter the `list` that `cheese` refers to, the `list` that `spam` refers to is also changed, because both `cheese` and `spam` refer to the same `list`."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["
\n","
source: https://automatetheboringstuff.com/2e/chapter4/
"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["You may be wondering why the weird behavior with mutable `lists` in the previous section doesn’t happen with immutable values like `integers` or `strings`. Let us elaborate on this topics."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Like `integer`, `'Hello'` is a `string` which is immutable and cannot be changed. If you \"change\" the `string` in a variable, a new `string` object is being made at a different place in memory, and the variable refers to this new `string`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["bacon = 'Hello'\n","print(id(bacon))\n","bacon = bacon + 'World'\n","print(id(bacon))"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["However, `lists` can be modified because they are mutable objects. The `append()` method doesn’t create a new `list` object; it changes the existing `list` object. We call this **\"modifying the object in-place.\"**"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["eggs = ['Hello'] # This creates a new list.\n","print(id(eggs))\n","eggs.append('World') # append() modifies the list \"in place\".\n","print(id(eggs)) # eggs still refers to the same list as before."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["If two variables refer to the same `list` (like `spam` and `cheese` in the previous section) and the `list` itself changes, both variables are affected because they both refer to the same `list`. The `append()`, `remove()`, `sort()`, `reverse()`, and other `list` methods modify their `lists` in place.\n","\n","> Python’s automatic garbage collector deletes any values not being referred to by any variables to free up memory. You don’t need to worry about how the garbage collector works, which is a good thing: manual memory management in other programming languages is a common source of bugs."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### Passing References"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["References are particularly important for understanding how arguments get passed to functions. When a function is called, **the values of the arguments are copied to the parameter variables**. For `lists` (and `dictionaries`, which we will describe in the next chapter), this means a **copy of the reference** is used for the parameter."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def eggs(someParameter):\n"," someParameter.append('Hello')\n","\n","spam = [1, 2, 3]\n","eggs(spam)\n","print(spam)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Notice that when `eggs()` is called, a return value is not used to assign a new value to `spam`. Instead, it modifies the list in place directly. Even though `spam` and `someParameter` contain separate references, they both refer to the same `list`. This is why the `append('Hello')` method call inside the function affects the `list` even after the function call has returned."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["For immutable types `string` and `integers`, we will create a new object in the function when we modify `someParameter`. Therefore, the original value will not be modified after the loop. "]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def eggs(someParameter):\n"," print(id(someParameter))\n"," someParameter = someParameter + \"world\" # This will create a new object and assign the new reference to someParameter\n"," print(id(someParameter))\n","\n","spam = \"hello\"\n","print(id(spam))\n","eggs(spam)\n","print(spam)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["### The `copy` Module’s `copy()` and `deepcopy()` Functions"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["`Python` provides a module named `copy` that provides both the `copy()` and `deepcopy()` functions. `copy()`, can be used to make a duplicate copy of a mutable value like a list or dictionary, not just a copy of a reference."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import copy\n","\n","spam = ['A', 'B', 'C', 'D']\n","print(id(spam))\n","cheese = copy.copy(spam)\n","print(id(cheese)) # cheese is a different list with different identity.\n","cheese[1] = 42\n","\n","spam, cheese"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Now the `spam` and `cheese` variables refer to separate `lists`, which is why only the list in `cheese` is modified when you assign 42 at index 1."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["
\n","
source: https://automatetheboringstuff.com/2e/chapter4/
"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["> If the list you need to copy contains another list, then use the `copy.deepcopy()` function instead of `copy.copy()`. The `deepcopy()` function will copy these inner lists as well."]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["> ### Exercise 2: Here, we will simulate the process of a simple card game. The game is played with a standard deck of 52 cards, and we will randomly select 40 cards and divide them evenly between two players. Each player gets a hand of 20 cards. The goal of the game is to collect pairs of cards with the same rank (e.g., two aces, two kings, etc.). The player with the most pairs at the end of the game wins.\n","\n","
"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import random\n","\n","# Write a function create_deck that creates a list of tuples representing a standard deck of 52 cards. \n","# Each tuple should contain two elements: the rank (e.g., \"ace\", \"king\", etc.) \n","# and the suit (e.g., \"hearts\", \"spades\", etc.). \n","def create_deck():\n"," ranks = [\"A\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"J\", \"Q\", \"K\"]\n"," suits = ['♣', '♦', '♥', '♠']\n"," deck = [(rank, suit) ______] # Use list comprehension to create the deck.\n"," return deck\n","\n","# A function that takes the deck as a parameter and returns two lists, each containing 26 randomly-selected \n","# cards from the deck. Use list slicing and the random module to implement this function.\n","def deal_cards(deck):\n"," deck = deck[:40]\n"," random.shuffle(deck)\n"," hand1 = _____ # Split it into 20 cards in each using slice\n"," hand2 = _____\n"," return hand1, hand2\n","\n","# Write a function find_pairs that takes a list of cards as a parameter and returns a list of tuples \n","# representing the pairs of cards in the list. A pair is defined as two cards with the same rank. \n","def find_pairs(cards):\n"," pairs = []\n"," for i, card1 in enumerate(cards):\n"," for j, card2 in enumerate(cards):\n"," if i != j and card1[0] == card2[0] and card1 not in [pair[0] for pair in pairs]\\\n"," and card1 not in [pair[1] for pair in pairs] and card2 not in [pair[0] for pair in pairs]\\\n"," and card2 not in [pair[1] for pair in pairs]:\n"," pairs._____((card1, card2)) # Use a method from the list to add it into the pairs\n"," return pairs\n","\n","deck = create_deck()\n","hand1, hand2 = deal_cards(deck)\n","pairs1 = find_pairs(hand1)\n","pairs2 = find_pairs(hand2)\n","\n","print(pairs1)\n","print(pairs2)\n","if ___________: # Compare the length of the two lists\n"," print(\"Player 1 wins!\")\n","elif _____________:\n"," print(\"Player 2 wins!\")\n","else:\n"," print(\"It's a tie!\")"]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"nSxBNltDtEvt"},"source":["`Lists` are useful data types since they allow you to write code that works on a modifiable number of values in a single variable. Later on, you will see programs using `lists` to do things that would be difficult or impossible to do without them.\n","\n","`Lists` are a sequence data type that is mutable, meaning that their contents can change. `Tuples` and `strings`, though also sequence data types, are immutable and cannot be changed. A variable that contains a `tuple` or `string` value can be overwritten with a new `tuple` or `string` value, but this is not the same thing as modifying the existing value in place — like, say, the `append()` or `remove()` methods do on `lists`. Because `tuples` are immutable, they don’t provide methods like `sort()` and `reverse()`, which modify existing `lists`. However `Python` provides the built-in functions `sorted()` and `reversed()`, which take any sequence as a parameter and return a new sequence with the same elements in a different order.\n","\n","Variables do not store `list` objects directly; they store references to `lists`. This is an important distinction when you are copying variables or passing `lists` as arguments in function calls. Because the value that is being copied is the list reference, be aware that any changes you make to the `list` might impact another variable in your program. You can use `copy()` or `deepcopy()` if you want to make changes to a `list` in one variable without modifying the original `list`. It is noted that slicing also create a new `list` object.\n","\n"]}],"metadata":{"colab":{"collapsed_sections":["SwFKFBMwRzoa","n3ezAAdIsgpj","gASYx5-Cxzyg","2kLRVhae3rSa","DPpfFEA07W0A","uloOTsPL9A74","MTJO4K049nuU","3hD9uBt6AW9l","IvviPig3At12","HQK5zGP749m3","7YVyJ-IgBZ7s","L5VlI3VKg_dV","wgqoOGqEiUre","jscaE3K65HP9","UMo588t4rY-O","AGs4jgnSsZa1"],"provenance":[],"toc_visible":true},"kernelspec":{"display_name":"base","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.0"}},"nbformat":4,"nbformat_minor":0}